#![deny(unsafe_code)]
use crossterm::{
    cursor::{Hide, MoveTo, MoveToNextLine},
    event::{read, Event, KeyCode},
    execute,
    style::*,
    terminal::*,
};
use lazy_static::lazy_static;
use std::io::stdout;
use std::io::Result;
use std::process::exit;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

// 清屏函数，将光标移动到(0, 0)位置
fn clear() {
    // 执行打印操作，将光标移动到(0, 0)位置
    let _t = execute!(stdout(), MoveTo(0, 0));
}

// 游戏状态
struct Game {
    // 当前游戏状态
    state: GameState,
    // 游戏接口
    interface: InterFace,
    // 方块阻塞矩阵
    blocks: [[u8; 10]; 20],
    // 当前俄罗斯方块
    curter: Tetris,
    // 下一个俄罗斯方块
    nxtter: Tetris,
    // 分数
    scores: u128,
}

impl Game {
    /// 创建一个新的游戏实例。
    ///
    /// # 返回值
    /// 返回一个 `Game` 对象。
    fn new() -> Self {
        Game {
            state: GameState::Stopped,   // 游戏状态为停止状态
            interface: InterFace::new(), // 创建一个新的用户接口
            blocks: [[0; 10]; 20],       // 创建一个 10x20 的方块矩阵，初始值都为 0
            curter: Tetris::new(),       // 创建一个新的俄罗斯方块对象
            nxtter: Tetris::new(),       // 创建一个新的俄罗斯方块对象
            scores: 0,                   // 分数为 0
        }
    }
    // 向下移动当前的俄罗斯方块
    fn down(&mut self) {
        // 标记是否已经到达底部
        let mut bottom = false;
        // 遍历后续的俄罗斯方块形状
        for points in &TETRIS[self.curter.kind][2..] {
            // 获取当前俄罗斯方块在屏幕上的位置
            let (xt, yt) = xt_yt(points, &self.curter);
            // 获取下一个位置
            let xt = xt + 1;
            // 如果下一个位置不合法，则表示已经到达底部
            if !(xt < 0 || xt < 20 && self.blocks[xt as usize][yt as usize] == 0) {
                bottom = true;
                break;
            }
        }
        // 如果到达底部
        if bottom {
            // 再次遍历后续的俄罗斯方块形状
            for points in &TETRIS[self.curter.kind][2..] {
                // 获取当前俄罗斯方块在屏幕上的位置
                let (xt, yt) = xt_yt(points, &self.curter);
                // 如果当前俄罗斯方块的位置不合法，则游戏结束
                if xt < 0 || yt < 0 {
                    self.state = GameState::Stopped;
                    self.show_all();
                    return;
                }
                // 将当前俄罗斯方块放置到屏幕上相应的位置
                self.blocks[xt as usize][yt as usize] = self.curter.color;
            }
            // 清除路径上的方块
            let mut cleanpath = 0;
            for x in 0..20 {
                let mut filled = true;
                for y in 0..10 {
                    filled = filled & (self.blocks[x][y] != 0);
                }
                // 如果一行方块都填充了，则清除路径层数加一
                if filled {
                    cleanpath += 1;
                    for i in (1..=x).rev() {
                        self.blocks[i] = self.blocks[i - 1];
                    }
                    self.blocks[0] = [0; 10];
                }
            }
            // 根据清除路径层数计算得分
            if cleanpath > 0 {
                self.scores += 20u128.pow(cleanpath);
            }
            // 交换当前俄罗斯方块和下一俄罗斯方块的位置
            std::mem::swap(&mut self.curter, &mut self.nxtter);
            self.nxtter = Tetris::new();
        } else {
            // 否则，向下移动当前俄罗斯方块
            self.curter.position[0] += 1;
        }
    }
    // 垂直移动当前游戏块器
    fn vertical(&mut self, moved: i32) {
        // 遍历TETRIS数组中从第三个点开始的每个点
        for points in &TETRIS[self.curter.kind][2..] {
            // 获取点相对于当前游戏块器的x和y坐标
            let (xt, yt) = xt_yt(points, &self.curter);
            // 计算移动后的新的y坐标
            let yt = yt + moved;
            // 检查新的y坐标是否超出边界或者新的x和y坐标是否不满足方块约束条件
            if (yt < 0 || yt >= 10)
                || !(xt < 0 || xt < 20 && self.blocks[xt as usize][yt as usize] == 0)
            {
                // 如果任何一个条件不满足，则返回
                return;
            }
        }
        // 更新当前游戏块器的位置的y坐标
        self.curter.position[1] += moved;
    }
    // 向右旋转当前的 curter
    fn turn(&mut self) {
        let _ = &self.curter.turn_right(); // 调用 curter 的 turn_right 方法使 curter 向右旋转
                                           // 检查 TETRIS 数组中 curter 当前形状之后的所有点
        for points in &TETRIS[self.curter.kind][2..] {
            let (xt, yt) = xt_yt(points, &self.curter);
            // 如果存在不合法情况，则使 curter 向左旋转并返回
            if (yt < 0 || yt >= 10)
                || !(xt < 0 || xt < 20 && self.blocks[xt as usize][yt as usize] == 0)
            {
                let _ = &self.curter.turn_left(); // 调用 curter 的 turn_left 方法使 curter 向左旋转
                return; // 函数执行结束并返回
            }
        }
    }

    // 显示游戏的当前状态
    fn show_all(&self) {
        self.interface.show_frame(
            &self.curter, // 当前的 curter
            &self.nxtter, // 下一个将要落下的 curter
            &self.blocks, // blocks 状态
            &self.state,  // state 状态
            self.scores,  // scores 状态
        );
    }
}

lazy_static! {
    // 静态变量：颜色
    static ref COLORTURN: Arc<Mutex<u8>> = Arc::new(Mutex::new(8));
    // 静态变量：线程数
    static ref THREAD_COUNT: Arc<Mutex<usize>> = Arc::new(Mutex::new(0));
    // 静态变量：游戏
    static ref GAME: Arc<Mutex<Game>> = Arc::new(Mutex::new(Game {
        // 游戏状态
        state: GameState::Stopped,
        // 界面配置
        interface: InterFace {
            width: 70,
            height: 30,
            interface: vec![],
        },
        // 方块数组
        blocks: [[0; 10]; 20],
        // 当前方块
        curter: Tetris {
            kind: 0,
            position: [-2, 5],
            direc: 0,
            color: 0,
        },
        // 下一个方块
        nxtter: Tetris {
            kind: 0 as usize,
            position: [-2, 5],
            direc: 0 as usize,
            color: 0,
        },
        // 分数
        scores: 0,
    }));
}
const TETRIS: [[[i32; 2]; 6]; 7] = [
    // 数据格式：前两个为[x上限下限]，[y上限下限]，后面余下的为相对于旋转中心的坐标
    [[1, 0], [1, -1], [0, 0], [0, -1], [0, 1], [1, 0]], // T
    [[1, 0], [1, -1], [0, 0], [0, 1], [1, 0], [1, -1]], // S
    [[1, 0], [1, -1], [0, 0], [0, -1], [1, 0], [1, 1]], // Z
    [[1, -1], [0, -1], [0, 0], [-1, 0], [1, 0], [1, -1]], // J
    [[1, -1], [0, 1], [0, 0], [-1, 0], [1, 0], [1, 1]], // L
    [[1, -2], [0, 0], [0, 0], [-1, 0], [1, 0], [-2, 0]], // I
    [[1, 0], [1, 0], [0, 0], [1, 0], [0, 1], [1, 1]],   // O
];

#[derive(PartialEq, Clone)]
// 游戏状态枚举
enum GameState {
    // 停止状态
    Stopped,
    // 正在运行状态
    Playing,
    // 暂停状态
    Pausing,
}

// 计算xt和yt坐标
fn xt_yt(points: &[i32; 2], t: &Tetris) -> (i32, i32) {
    (
        // (x0,y0)
        // (y0,-x0)
        t.position[0] as i32
            + match t.direc {
                0 => points[0],  // 当方向为向上时，xt坐标为points[0]
                1 => -points[1], // 当方向为向右时，xt坐标为-points[1]
                2 => -points[0], // 当方向为向下时，xt坐标为-points[0]
                _ => points[1],  // 当方向为向左时，xt坐标为points[1]
            } as i32,
        t.position[1] as i32
            + match t.direc {
                0 => points[1],  // 当方向为向上时，yt坐标为points[1]
                1 => points[0],  // 当方向为向右时，yt坐标为points[0]
                2 => -points[1], // 当方向为向下时，yt坐标为-points[1]
                _ => -points[0], // 当方向为向左时，yt坐标为-points[0]
            } as i32,
    )
}

// 绘制外部轮廓线
// 定义一个结构体 `Framework`，
// 其中包含三个字段：
// - `height`: 表示框架的高度，类型为 `usize`
// - `width`: 表示框架的宽度，类型为 `usize`
// - `stringify`: 表示框架的字符串化形式，是一个二维向量，每个元素的类型为 `usize`
struct FrameWork {
    height: usize,
    width: usize,
    stringify: Vec<Vec<usize>>,
}

// FrameWork 类是一个二维终端图形渲染框架的实现
impl FrameWork {
    // 构造函数，接受水平方向和垂直方向的尺寸作为参数
    fn new(horizontal: usize, vertical: usize) -> Self {
        // 创建一个具有指定尺寸的 FrameWork 对象，并初始化 stringify 属性为指定尺寸的二维向量，其中每个元素初始化为0
        let mut temp = FrameWork {
            height: horizontal,
            width: vertical,
            stringify: vec![vec![0; vertical]; horizontal],
        };
        // 调用 draw 方法绘制图形
        temp.draw(0, 0, vertical - 1, horizontal - 1);
        // 返回创建的 FrameWork 对象
        temp
    }

    // 重绘图形
    fn rdraw(&mut self, up: usize, down: usize, left: usize, right: usize) {
        // 判断参数是否超出图形范围或者左右边界相等或者上下边界相等，如果满足任一条件，则输出错误提示并返回
        if up >= self.height
            || down >= self.height
            || left >= self.width
            || right >= self.width
            || left == right
            || up == down
        {
            eprintln!("\nThe Parameters You Had Given Is Incorrent!Function Draw will not work!\nTraceBack:\n\tleft:{} right:{} width_limit:{}\n\tup:{} down:{} height_limit:{}\n", left, right, self.width, up, down, self.height);
            return;
        }
        // 对指定的区域进行重绘
        for i in (up + 1)..down {
            self.stringify[i][left] |= 3;
            self.stringify[i][right] |= 3;
        }
        for i in (left + 1)..right {
            self.stringify[up][i] |= 12;
            self.stringify[down][i] |= 12;
        }
        self.stringify[up][left] |= 6;
        self.stringify[up][right] |= 10;
        self.stringify[down][left] |= 5;
        self.stringify[down][right] |= 9;
    }

    // 绘制图形，是对 rdraw 方法的封装
    fn draw(&mut self, x: usize, y: usize, width: usize, height: usize) {
        self.rdraw(
            self.height - 1 - y - height,
            self.height - 1 - y,
            x,
            x + width,
        );
    }

    // 获取图形的渲染结果
    fn get_vec(&self) -> Vec<Vec<char>> {
        // 定义字符对应的二进制表示
        let vs = [
            ' ', '上', '下', '║', '左', '╚', '╔', '╠', '右', '╝', '╗', '╣', '═', '╩', '╦',
            '╬',
            // 二进制表示
        ];
        // 将 stringify 的每个元素映射为对应的字符，放入 ret 向量
        let mut ret = vec![];
        for s in self.stringify.iter() {
            ret.push(s.iter().map(|&x| vs[x]).collect::<Vec<char>>());
        }
        // 返回渲染结果
        ret
    }
}

// 向写入字符的接口中写入文字
fn write(interface: &mut Vec<Vec<char>>, left: usize, top: usize, words: String) {
    assert!(left + words.len() < interface[0].len());
    for (i, ch) in words.chars().enumerate() {
        interface[top][i + left] = ch;
    }
}

// 向带有样式的接口中写入文字
fn write_styled(
    interface: &mut Vec<Vec<StyledContent<char>>>,
    left: usize,
    top: usize,
    words: String,
) {
    assert!(left + words.len() < interface[0].len());
    for (i, ch) in words.chars().enumerate() {
        interface[top][i + left] = style(ch);
    }
}

struct InterFace {
    interface: Vec<Vec<char>>,
    width: usize,
    height: usize,
}

impl InterFace {
    fn new() -> Self {
        // 创建一个 InterFace 对象，并设置初始的 width、height 和 interface
        let mut temp = InterFace {
            width: 70,
            height: 30,
            interface: vec![],
        };

        // 创建一个 FrameWork 对象，并使用 temp 的 height 和 width 进行初始化
        let mut frame = FrameWork::new(temp.height, temp.width);

        // 在 frame 中绘制三个矩形
        frame.rdraw(0, 29, 0, 39);
        frame.rdraw(6, 27, 9, 30);
        frame.rdraw(4, 12, 48, 61);

        // 将 frame 绘制的矩形保存到 temp 的 interface 中
        temp.interface = frame.get_vec();

        // 在 temp 的 interface 中添加文本内容
        write(&mut temp.interface, 49, 2, "Next Tetris".to_string());
        write(&mut temp.interface, 43, 14, "Operations:".to_string());
        write(&mut temp.interface, 43, 16, "space:".to_string());
        write(&mut temp.interface, 47, 17, "pause the game".to_string());
        write(&mut temp.interface, 43, 18, "q:".to_string());
        write(&mut temp.interface, 47, 19, "quit the game".to_string());
        write(&mut temp.interface, 43, 20, "↑↓←→:".to_string());
        write(
            &mut temp.interface,
            47,
            21,
            "control the tertris".to_string(),
        );

        // 返回创建并初始化的 temp 对象
        temp
    }
    // 显示游戏框架
    fn show_frame(
        &self,
        t: &Tetris,              // 当前方块
        next: &Tetris,           // 下一个方块
        blocks: &[[u8; 10]; 20], // 游戏板
        state: &GameState,       // 游戏状态
        scores: u128,            // 分数
    ) {
        clear(); // 清空屏幕

        // 加载游戏界面样式
        let mut interface = vec![];
        for lines in &self.interface {
            let mut line = vec![];
            for &ch in lines {
                line.push(style(ch)) // 根据样式创建字符
            }
            interface.push(line); // 添加行到界面
        }

        // 绘制当前方块和下下一个方块的得分
        for points in &TETRIS[t.kind][2..] {
            let (xt, yt) = xt_yt(points, t); // 当前方块得分位置
            if xt >= 0 && xt < 20 && yt >= 0 && yt < 10 {
                interface[xt as usize + 7][(yt as usize) * 2 + 10] =
                    style('█').with(Color::AnsiValue(t.color));
                interface[xt as usize + 7][(yt as usize) * 2 + 11] =
                    style('█').with(Color::AnsiValue(t.color));
            }
        }
        for points in &TETRIS[next.kind][2..] {
            interface[(points[0] + 8) as usize][(points[1] * 2 + 54) as usize] =
                style('█').with(Color::AnsiValue(next.color));
            interface[(points[0] + 8) as usize][(points[1] * 2 + 55) as usize] =
                style('█').with(Color::AnsiValue(next.color));
        }

        // 绘制游戏板方块
        for x in 0..20 {
            for y in 0..10 {
                if blocks[x][y] >= 1 {
                    interface[x as usize + 7][(y as usize) * 2 + 10] =
                        style('█').with(Color::AnsiValue(blocks[x][y]));
                    interface[x as usize + 7][(y as usize) * 2 + 11] =
                        style('█').with(Color::AnsiValue(blocks[x][y]));
                }
            }
        }

        // 显示游戏状态和分数
        write_styled(
            &mut interface,
            17,
            5,
            match state {
                GameState::Playing => "Playing", // 如果是游戏进行中状态，则显示"Playing"
                GameState::Stopped => "You loose!", // 如果游戏停止状态，则显示"你输了！"
                GameState::Pausing => "Pausing", // 如果是暂停状态，则显示"暂停"
            }
            .to_string(),
        );
        write_styled(&mut interface, 16, 3, format!("Scores: {}", scores)); // 显示分数

        // 输出界面
        for line in &interface {
            for &ch in line {
                print!("{}", ch);
            }
            let _ = execute!(stdout(), MoveToNextLine(1)); // 移动到下一行
        }
    }
}

#[derive(Clone)]
struct Tetris {
    pub kind: usize,
    // 0-7分别表示 TSZJLIO型方块
    pub position: [i32; 2],
    // 表示了在游戏中的位置
    pub direc: usize,
    // 表示了方块在游戏中的指向，0为初始方向，1-3依次顺时针旋转90°
    pub color: u8, // 表示了方块在游戏中的颜色
}

// Tetris类的实现
impl Tetris {
    // 创建一个新的Tetris对象
    fn new() -> Self {
        Tetris {
            // 类型
            kind: (rand::random::<u8>() % 7) as usize,
            // 位置
            position: [-2, 5],
            // 方向
            direc: (rand::random::<u8>() % 4) as usize,
            // 颜色
            color: {
                let mut color_turn_lock = COLORTURN.lock().unwrap();
                *color_turn_lock = (*color_turn_lock + 1) % 8;
                *color_turn_lock + 1
            },
        }
    }

    // 向右旋转
    fn turn_right(&mut self) {
        self.direc += 1;
        self.direc %= 4;
    }

    // 向左旋转
    fn turn_left(&mut self) {
        self.direc += 3;
        self.direc %= 4;
    }
}

// 判断是否启用原始模式成功，如果失败则输出提示信息并退出程序
fn main() -> Result<()> {
    if let Err(_) = enable_raw_mode() {
        println!("Your terminal does not support raw mode, please try another terminal or visit https://docs.rs/crossterm/0.23.0/crossterm/terminal/#raw-mode for more help. Please Not Run This Program Directly in CLion/Intellij IDEA 's Run Tag!");
        exit(0);
    }

    // 获取标准输出流
    let mut stdout = stdout();

    // 执行命令，进入备用屏幕模式
    execute!(stdout, EnterAlternateScreen, Hide)?;

    // 匿名函数，用于切换游戏状态
    fn trans() {
        let mut begin_new_thread = false;

        {
            // 对游戏状态进行互斥锁操作
            let mut game_lock = GAME.lock().unwrap();

            // 如果游戏状态为Playing，则改为Pausing，并显示所有状态
            if game_lock.state == GameState::Playing {
                game_lock.state = GameState::Pausing;
                game_lock.show_all();
            } else {
                // 如果游戏状态为Stopped，则重新初始化游戏状态
                if game_lock.state == GameState::Stopped {
                    *game_lock = Game::new();
                }

                // 设置游戏状态为Playing，并显示所有状态
                game_lock.state = GameState::Playing;
                game_lock.show_all();

                // 开始新的线程
                begin_new_thread = true;
            }
        }

        // 如果需要开始新的线程
        if begin_new_thread {
            // 创建新的线程，执行循环操作
            thread::spawn(move || {
                let _thread_holder = THREAD_COUNT.lock().unwrap();
                loop {
                    // 每隔500毫秒执行一次
                    std::thread::sleep(Duration::from_millis(500));
                    let mut game_lock = GAME.lock().unwrap();
                    // 如果游戏状态不是Playing，则退出线程
                    if game_lock.state != GameState::Playing {
                        return;
                    }
                    game_lock.down();
                    game_lock.show_all();
                }
            });
        }
    }

    // 执行命令，清除所有内容
    let _ = execute!(stdout, Clear(ClearType::All));

    // 切换游戏状态
    trans();

    // 循环读取终端事件
    loop {
        let event = read()?;
        // 如果按下的键为字符键
        if let Event::Key(_keyevent) = event {
            match _keyevent.code {
                // 如果按下的字符为q，则退出程序
                KeyCode::Char(ch) => match ch {
                    'q' => break,
                    ' ' => trans(),
                    _ => (),
                },
                // 如果按下的字符为Up，则游戏上一回合
                KeyCode::Up => {
                    let mut game_lock = GAME.lock().unwrap();
                    if game_lock.state == GameState::Playing {
                        game_lock.turn();
                        game_lock.show_all();
                    }
                }
                // 如果按下的字符为Down，则游戏下一回合
                KeyCode::Down => {
                    let mut game_lock = GAME.lock().unwrap();
                    if game_lock.state == GameState::Playing {
                        game_lock.down();
                        game_lock.show_all();
                    }
                }
                // 如果按下的字符为Left，则游戏垂直移动上一格
                KeyCode::Left => {
                    let mut game_lock = GAME.lock().unwrap();
                    if game_lock.state == GameState::Playing {
                        game_lock.vertical(-1);
                        game_lock.show_all();
                    }
                }
                // 如果按下的字符为Right，则游戏垂直移动下一格
                KeyCode::Right => {
                    let mut game_lock = GAME.lock().unwrap();
                    if game_lock.state == GameState::Playing {
                        game_lock.vertical(1);
                        game_lock.show_all();
                    }
                }
                _ => {}
            }
        }
    }

    // 执行命令，离开备用屏幕模式
    execute!(stdout, LeaveAlternateScreen)?;

    // 禁用原始模式
    disable_raw_mode()?;

    // 返回成功
    Ok(())
}
